#include "ST7735_TFT.h"

uint8_t ST7735_Base::send_cmd(uint8_t cmd)
{
    gpio_write(dc, 0);
    spi->loadSlave(cs);
    spi->write(cmd);
    spi->deloadSlave();
    return 1;
}

uint8_t ST7735_Base::send_data(uint8_t data)
{
    gpio_write(dc, 1);
    spi->loadSlave(cs);
    spi->write(data);
    spi->deloadSlave();
    return 1;
}

uint8_t ST7735_Base::send_data16(uint16_t data16)
{
    gpio_write(dc, 1);
    spi->loadSlave(cs);
    spi->write16(data16);
    spi->deloadSlave();
    return 1;
}
uint8_t ST7735_Base::send_data16(uint16_t data16, int count)
{
    gpio_write(dc, 1);
    spi->loadSlave(cs);
    spi->write16(data16, count);
    spi->deloadSlave();
    return 1;
}
uint8_t ST7735_Base::send_data16(const uint16_t *buf, int size)
{
    gpio_write(dc, 1);
    spi->loadSlave(cs);
    spi->write16(buf, size);
    spi->deloadSlave();
    return 1;
}

void ST7735_Base::init()
{
    send_cmd(0x01);
    delay_ms(50);

    send_cmd(0x11); // Sleep out
    delay_ms(120);

    //----ST7735S Frame Rate---------------------//
    send_cmd(0xB1);  // Frame rate 80Hz Frame rate=333k/((RTNA + 20) x (LINE + FPA + BPA))
    send_data(0x02); // RTNA
    send_data(0x35); // FPA
    send_data(0x36); // BPA
    send_cmd(0xB2);  // Frame rate 80Hz
    send_data(0x02);
    send_data(0x35);
    send_data(0x36);
    send_cmd(0xB3); // Frame rate 80Hz
    send_data(0x02);
    send_data(0x35);
    send_data(0x36);
    send_data(0x02);
    send_data(0x35);
    send_data(0x36);

    //------------------------------------Display Inversion Control-----------------------------------------//
    send_cmd(0xB4);
    send_data(0x03);

    //------------------------------------ST7735S Power Sequence-----------------------------------------//
    send_cmd(0xC0);
    send_data(0xA2);
    send_data(0x02);
    send_data(0x84);
    send_cmd(0xC1);
    send_data(0xC5);
    send_cmd(0xC2);
    send_data(0x0D);
    send_data(0x00);
    send_cmd(0xC3);
    send_data(0x8D);
    send_data(0x2A);
    send_cmd(0xC4);
    send_data(0x8D);
    send_data(0xEE);
    //---------------------------------End ST7735S Power Sequence---------------------------------------//
    send_cmd(0xC5); // VCOM
    send_data(0x0a);
    send_cmd(0x36); // MX=1, MY=1, RGB=0 mode
    send_data(0xC4);
    //------------------------------------ST7735S Gamma Sequence-----------------------------------------//
    send_cmd(0XE0);
    send_data(0x12);
    send_data(0x1C);
    send_data(0x10);
    send_data(0x18);
    send_data(0x33);
    send_data(0x2C);
    send_data(0x25);
    send_data(0x28);
    send_data(0x28);
    send_data(0x27);
    send_data(0x2F);
    send_data(0x3C);
    send_data(0x00);
    send_data(0x03);
    send_data(0x03);
    send_data(0x10);
    send_cmd(0XE1);
    send_data(0x12);
    send_data(0x1C);
    send_data(0x10);
    send_data(0x18);
    send_data(0x2D);
    send_data(0x28);
    send_data(0x23);
    send_data(0x28);
    send_data(0x28);
    send_data(0x26);
    send_data(0x2F);
    send_data(0x3B);
    send_data(0x00);
    send_data(0x03);
    send_data(0x03);
    send_data(0x10);
    //------------------------------------End ST7735S Gamma Sequence-----------------------------------------//
    send_cmd(0x3A); // 65k mode
    send_data(0x05);
    send_cmd(0x29); // Display on
    delay_ms(120);

    send_cmd(0x10);
}

int ST7735_Base::begin()
{
    spi->begin();

    gpio_init(dc, OUT_PP);
    gpio_init(cs, OUT_PP);
    gpio_write(cs, 1);

    if (rst != Pin_null)
    {
        gpio_init(rst, OUT_PP);
        gpio_write(rst, 1);
        delay_ms(50);
        gpio_write(rst, 0);
        delay_ms(50);
        gpio_write(rst, 1);
        delay_ms(50);
    }

    if (bl != Pin_null)
    {
        gpio_init(bl, OUT_PP);
    }

    init();
    clear();
    return 1;
}

void ST7735_Base::clear(uint16_t color)
{
    send_cmd(0x2a);
    send_data16(0);
    send_data16(131);

    send_cmd(0x2b);
    send_data16(0);
    send_data16(161);

    send_cmd(0x2c);
    send_data16(color, 162 * 132);
}
/******************************************************/

void ST7735_GUI::setDrawArea(int x, int y, int x_e, int y_e)
{
    int16_t t;
    switch (rotation)
    {
    case 1:
        t = x;
        x = WIDTH - 1 - y;
        y = t;
        break;
    case 2:
        x = WIDTH - 1 - x;
        y = HEIGHT - 1 - y;
        break;
    case 3:
        t = x;
        x = y;
        y = HEIGHT - 1 - t;
        break;
    }

    switch (rotation)
    {
    case 1:
        t = x_e;
        x_e = WIDTH - 1 - y_e;
        y_e = t;
        break;
    case 2:
        x_e = WIDTH - 1 - x_e;
        y_e = HEIGHT - 1 - y_e;
        break;
    case 3:
        t = x_e;
        x_e = y_e;
        y_e = HEIGHT - 1 - t;
        break;
    }

    x = x < 0 ? 0 : x;
    y = y < 0 ? 0 : y;

    x_e = (x_e > 128 ? 127 : x_e);
    y_e = (y_e > 160 ? 159 : y_e);

    if (x > x_e)
    {
        t = x;
        x = x_e;
        x_e = t;
    }
    if (y > y_e)
    {
        t = y;
        y = y_e;
        y_e = t;
    }

    send_cmd(0x2a);
    send_data16(x + 2);
    send_data16(x_e + 2);

    send_cmd(0x2b);
    send_data16(y + 1);
    send_data16(y_e + 1);

    send_cmd(0x2c);
}

void ST7735_GUI::show()
{
    send_cmd(0x2a);
    send_data(0x00);
    send_data(0 + 2);
    send_data(0x00);
    send_data(127 + 2);

    send_cmd(0x2b);
    send_data(0x00);
    send_data(0 + 1);
    send_data(0x00);
    send_data(159 + 1);

    send_cmd(0x2c);
    // send_data(getBuffer(), 128 * 160);
}

void ST7735_GUI::drawPixel(int16_t x, int16_t y, uint16_t color)
{
    setDrawArea(x, y, x, y);
    send_data16(color);
}

void ST7735_GUI::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
    setDrawArea(x, y, x, y + h - 1);
    send_data16(color, h);
}

void ST7735_GUI::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
    setDrawArea(x, y, x + w - 1, y);
    send_data16(color, w);
}
void ST7735_GUI::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
    setDrawArea(x, y, x + w - 1, y + h - 1);
    send_data16(color, w * h);
}
void ST7735_GUI::fillScreen(uint16_t color)
{
    clear(color);
}

void ST7735_GUI::showRGBImageFast(int16_t x, int16_t y, uint16_t *bitmap,
                                  int16_t w, int16_t h)
{
    for (int i = 0; i < h; ++i)
    {
        setDrawArea(x, y + i, x + w - 1, y + i);
        bitmap += w;
        send_data16(bitmap, w);
        // delay_ms(100);
    }
}
